/*
 * @Copyright: 2017 www.yyfax.com Inc. All rights reserved.
 */
package com.yyfax.pay.order.service;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.yyfax.commons.json.FastJsonUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.service.CacheService;
import com.yyfax.framework.yyfax.YYFaxSignUtil;
import com.yyfax.framework.yyfax.enums.UserTypeEnum;
import com.yyfax.framework.yyfax.enums.YesNoEnum;
import com.yyfax.pay.common.constants.CacheName;
import com.yyfax.pay.common.enums.NotifyTaskTypeEnum;
import com.yyfax.pay.common.enums.PayModeEnum;
import com.yyfax.pay.common.enums.PayRspCodeEnum;
import com.yyfax.pay.common.enums.QueryTaskTypeEnum;
import com.yyfax.pay.common.enums.TaskStateEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.exception.YYPayException;
import com.yyfax.pay.common.util.CheckSignUtils;
import com.yyfax.pay.config.data.service.BankConfigService;
import com.yyfax.pay.config.data.service.MerchantConfigService;
import com.yyfax.pay.config.data.service.PaymentQuotaService;
import com.yyfax.pay.config.data.service.PaymentRouteService;
import com.yyfax.pay.config.model.po.BankConfigPO;
import com.yyfax.pay.config.model.po.MerchantConfigPO;
import com.yyfax.pay.config.model.po.PaymentQuotaPO;
import com.yyfax.pay.config.model.po.PaymentRoutePO;
import com.yyfax.pay.order.model.po.NotifyTaskPO;
import com.yyfax.pay.order.model.po.QueryTaskPO;
import com.yyfax.pay.order.model.to.bank.QueryBankQuotaContent;
import com.yyfax.pay.order.model.to.bank.QueryBankQuotaListContent;
import com.yyfax.pay.order.model.to.bank.QueryBankQuotaParams;
import com.yyfax.pay.quartz.service.PayTaskService;
import com.yyfax.pay.third.model.to.yyfax.QueryCardBinContent;
import com.yyfax.pay.third.model.to.yyfax.QueryCardBinParams;
import com.yyfax.pay.third.service.ThirdPayService;

/**
 * 支付模块公用服务
 * 
 * @author buyi
 * @date 2017-04-20 13:34:45
 * @since v1.0.0
 */
@Service
public class PayCommonService {
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private PayTaskService payTaskService;

	@Autowired
	private AdapterService adapterService;

	@Autowired
	private RouteService routeService;

	@Autowired
	private PaymentRouteService paymentRouteService;

	@Autowired
	private PaymentQuotaService paymentQuotaService;

	@Autowired
	private BankConfigService bankConfigService;

	@Resource(name = "payCacheService")
	private CacheService cacheService;

	@Autowired
	private MerchantConfigService merchantConfigService;

	/**
	 * 保存查询任务
	 * 
	 * @author buyi
	 * @date 2017-04-20 13:42:35
	 * @since v1.0.0
	 * @param merchantCode
	 * @param orderId
	 * @param serialNo
	 * @param taskType
	 * @param tradeOrderId
	 * @param paymentCode
	 * @param startTime
	 * @param endTime
	 * @param maxNo
	 * @param remark
	 * @throws Exception
	 */
	public void saveQueryTask(String merchantCode, String orderId, String serialNo, QueryTaskTypeEnum taskType, String tradeOrderId,
			String paymentCode, String startTime, String endTime, int maxNo, String remark) throws Exception {

		QueryTaskPO task = new QueryTaskPO();
		task.setMerchantCode(merchantCode);
		task.setOrderId(orderId);
		task.setSerialNo(serialNo);
		task.setType(taskType.getValue());
		task.setTradeOrderId(tradeOrderId);
		task.setPaymentCode(paymentCode);
		task.setStartTime(startTime); // 设置启动时间
		task.setEndTime(endTime);
		task.setOptNo(0);// 初始化查询次数
		task.setMaxNo(maxNo); // 最大查询次数
		task.setRemark(remark);
		task.setState(TaskStateEnum.ACTIVE.getValue());

		payTaskService.queryTask(task);
	}

	/**
	 * 保存异步通知任务
	 * 
	 * @author buyi
	 * @date 2017-04-20 17:38:02
	 * @since v1.0.0
	 * @param merchantCode
	 * @param orderId
	 * @param serialNo
	 * @param taskType
	 * @param tradeOrderId
	 * @param notifyUrl
	 * @param reqContent
	 * @param startTime
	 * @param endTime
	 * @param maxNo
	 * @param remark
	 * @throws Exception
	 */
	public void saveNotifyTask(String merchantCode, String orderId, String serialNo, NotifyTaskTypeEnum taskType, String tradeOrderId,
			String notifyUrl, String reqContent, String startTime, String endTime, int maxNo, String remark) throws Exception {
		logger.debug("enter save notify task");

		NotifyTaskPO task = new NotifyTaskPO();
		task.setMerchantCode(merchantCode);
		task.setOrderId(orderId);
		task.setSerialNo(serialNo);
		task.setType(taskType.getValue());
		task.setTradeOrderId(tradeOrderId);
		task.setNotifyUrl(notifyUrl);
		task.setReqContent(reqContent);
		task.setStartTime(startTime); // 设置启动时间
		task.setEndTime(endTime);
		task.setOptNo(0); // 初始化通知次数
		task.setMaxNo(maxNo); // 最大查询次数
		task.setRemark(remark);
		task.setState(TaskStateEnum.ACTIVE.getValue());

		payTaskService.notifyTask(task);
	}

	/**
	 * 查询卡BIN
	 * 
	 * @author buyi
	 * @date 2017-04-13 18:44:32
	 * @since v1.0.0
	 * @param merchantCode
	 * @param cardNo
	 * @return
	 * @throws Exception
	 */
	public QueryCardBinContent queryCardBin(String merchantCode, String cardNo) throws Exception {
		if (StringUtils.isBlank(merchantCode) || StringUtils.isBlank(cardNo)) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_QUERY_CARD_BIN_ERROR, "参数merchantCode/cardNo为空");
		}

		ThirdPayService service = adapterService.adapterPayService(routeService.routeCardBin(merchantCode, cardNo));
		if (service == null) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_QUERY_CARD_BIN_ERROR, "卡bin服务没有找到");
		}

		QueryCardBinContent cardBin = service.queryCardBin(new QueryCardBinParams(merchantCode, cardNo));
		if (cardBin == null) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_QUERY_CARD_BIN_ERROR, "查询卡bin返回null");
		}

		if (!PayRspCodeEnum.SUCCESS.isValue(cardBin.getResultCode())) {// 卡bin查询异常
			throw new YYPayException(PayRspCodeEnum.fromValue(cardBin.getResultCode()), "查询卡bin响应码异常", "查询卡bin响应码异常");
		}

		return cardBin;
	}

	/**
	 * 获取商户银行限额<br>
	 * 默认查询主体类型为：个人，支付方式：认证支付
	 * 
	 * @author buyi
	 * @date 2017-05-02 16:15:38
	 * @since v1.0.0
	 * @param merchantCode
	 * @return
	 * @throws Exception
	 */
	public QueryBankQuotaListContent QueryBankQuotaList(String merchantCode) throws Exception {
		List<QueryBankQuotaContent> list = new ArrayList<>();

		// 表结构变更，默认个人、认证支付的限额,edit by yuch since 2.0.0
		String subjectType = UserTypeEnum.NATURAL_PERSON.getValue();
		String payMode = PayModeEnum.AUTH_PAY.getValue();

		List<PaymentRoutePO> paymentRoutePOs = paymentRouteService.queryByMerchantCode(merchantCode, subjectType, payMode);
		if (paymentRoutePOs != null) {
			for (PaymentRoutePO paymentRoutePO : paymentRoutePOs) {
				// PaymentQuotaPO quotaPO = paymentQuotaService.query(merchantCode, paymentRoutePO.getPaymentCode(), paymentRoutePO.getBankCode());
				PaymentQuotaPO quotaPO = paymentQuotaService.query(merchantCode, paymentRoutePO.getPaymentCode(), paymentRoutePO.getBankCode(),
						paymentRoutePO.getSubjectType(), paymentRoutePO.getPayMode());
				if (quotaPO == null) {
					continue;
				}

				BankConfigPO bankConfigPO = bankConfigService.queryByBankCode(paymentRoutePO.getBankCode());

				QueryBankQuotaContent queryBankQuotaContent = new QueryBankQuotaContent();
				queryBankQuotaContent.setBankName(bankConfigPO != null ? bankConfigPO.getBankName() : "");
				queryBankQuotaContent.setEnName(bankConfigPO != null ? bankConfigPO.getEnName() : "");
				queryBankQuotaContent.setSingleQuota(quotaPO.getSingleQuota());
				queryBankQuotaContent.setDayQuota(quotaPO.getDayQuota());
				queryBankQuotaContent.setMonthQuota(quotaPO.getMonthQuota());

				list.add(queryBankQuotaContent);
			}
		}

		QueryBankQuotaListContent content = new QueryBankQuotaListContent();
		content.setList(list);
		return content;
	}

	public QueryBankQuotaListContent queryBankQuotaList2(String merchantCode, QueryBankQuotaParams params) throws Exception {
		//
		String subjectType = params.getSubjectType();
		String payMode = params.getPayMode();

		List<QueryBankQuotaContent> list = new ArrayList<>();
		List<PaymentRoutePO> paymentRoutePOs = paymentRouteService.queryByMerchantCode(merchantCode, subjectType, payMode);

		if (paymentRoutePOs != null) {
			for (PaymentRoutePO paymentRoutePO : paymentRoutePOs) {
				if (!YesNoEnum.YES.isValue(paymentRoutePO.getState())) {
					// 过滤无效配置
					continue;
				}

				PaymentQuotaPO quotaPO = paymentQuotaService.query(merchantCode, paymentRoutePO.getPaymentCode(), paymentRoutePO.getBankCode(),
						paymentRoutePO.getSubjectType(), paymentRoutePO.getPayMode());
				if (quotaPO == null || !YesNoEnum.YES.isValue(quotaPO.getState())) {
					continue;
				}

				BankConfigPO bankConfigPO = bankConfigService.queryByBankCode(paymentRoutePO.getBankCode());
				if (bankConfigPO == null) {
					// 如果银行基础配置不存在
					continue;
				}

				QueryBankQuotaContent queryBankQuotaContent = new QueryBankQuotaContent();
				queryBankQuotaContent.setBankName(bankConfigPO.getBankName());
				queryBankQuotaContent.setAbbrName(bankConfigPO.getAbbrName());
				queryBankQuotaContent.setEnName(bankConfigPO.getEnName());
				queryBankQuotaContent.setBankCode(bankConfigPO.getBankCode());
				queryBankQuotaContent.setSingleQuota(quotaPO.getSingleQuota());
				queryBankQuotaContent.setDayQuota(quotaPO.getDayQuota());
				queryBankQuotaContent.setMonthQuota(quotaPO.getMonthQuota());
				queryBankQuotaContent.setRemark(quotaPO.getRemark());

				list.add(queryBankQuotaContent);
			}
		}

		QueryBankQuotaListContent content = new QueryBankQuotaListContent();
		content.setList(list);
		return content;
	}

	/**
	 * 生成防重复提交token
	 * 
	 * @author buyi
	 * @date 2017-05-15 14:32:32
	 * @since v1.0.0
	 * @param tradeOderId
	 * @return
	 */
	public String generateDuplicateToken(String tradeOderId) {
		String token = UUID.randomUUID().toString();

		setDuplicateToken(tradeOderId, token);

		return token;
	}

	/**
	 * 校验重复提交
	 * 
	 * @author buyi
	 * @date 2017-05-13 18:37:33
	 * @since v1.0.0
	 * @param token
	 * @throws YYPayException
	 */
	public void checkAndDelDuplicateToken(String tradeOderId, String token) throws YYPayException {
		boolean isDuplicate = false;
		String key = getDuplicateTokenKey(tradeOderId, token);
		if (checkDuplicateToken(key, token)) {
			isDuplicate = cacheService.delete(key);
		}

		if (!isDuplicate) {// 如果是重复提交，则抛出异常
			throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_DUPLICATE_OPT, "用户重复操作");
		}
	}

	/**
	 * 获取缓存key
	 * 
	 * @author buyi
	 * @date 2017-05-13 19:06:09
	 * @since v1.0.0
	 * @param tradeOderId
	 * @param token
	 * @return
	 */
	public String getDuplicateTokenKey(String tradeOderId, String token) {
		return CacheName.DUPLICATE_FORM_SUBMIT.concat("_").concat(tradeOderId);
	}

	/**
	 * 检查重复操作token
	 * 
	 * @author buyi
	 * @date 2017-05-13 19:06:22
	 * @since v1.0.0
	 * @param key
	 * @param token
	 * @return
	 */
	public boolean checkDuplicateToken(String key, String token) {
		String targetToken = cacheService.get(key);

		return StringUtils.equals(targetToken, token);
	}

	public void setDuplicateToken(String tradeOderId, String token) {
		String key = getDuplicateTokenKey(tradeOderId, token);

		cacheService.set(key, token);
	}

	/**
	 * 验签
	 * 
	 * @author buyi
	 * @date 2017-08-29 16:42:19
	 * @since v2.0.0
	 * @param merchantCode
	 * @param params
	 * @throws Exception
	 */
	public boolean validSign(String merchantCode, Object params, String srcSign) {
		// 2.验签
		MerchantConfigPO merchantConfigPO;
		try {
			merchantConfigPO = merchantConfigService.queryByMerchantCode(merchantCode);
			if (merchantConfigPO == null || !YesNoEnum.YES.isValue(merchantConfigPO.getState())) {
				logger.error(Log.op("PayCommonService.validSign").msg("商户信息不存在，或者无效配置信息，验签不通过").kv("merchantCode", merchantCode).toString());
				return false;
			}

			return CheckSignUtils.checkSign2(params, merchantConfigPO.getSignKey(), srcSign);
		} catch (Exception e) {
			logger.error(Log.op("PayCommonService.validSign").msg("验签异常").kv("merchantCode", merchantCode).toString());
		}
		return false;
	}

	/**
	 * 获取签名值
	 * 
	 * @author buyi
	 * @date 2017-09-02 15:59:56
	 * @since v2.0.0
	 * @param merchantCode
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public String getSign(String merchantCode, Object params) throws Exception {
		MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(merchantCode);
		if (merchantConfigPO == null || !YesNoEnum.YES.isValue(merchantConfigPO.getState())) {
			throw PayExceptionCode.ERROR_ILL_PARAMETER.expMsg("merchantConfig 缺少配置,merchant:%s", merchantCode);
		}

		return CheckSignUtils.sign2(params, merchantConfigPO.getSignKey());
	}

	/**
	 * 获取签名值<br>
	 * params 序列化成json字符串
	 * 
	 * @author buyi
	 * @date 2017-09-08 11:07:37
	 * @since v2.0.0
	 * @param merchantCode
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public String getSignJson(String merchantCode, Object params) throws Exception {
		MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(merchantCode);
		if (merchantConfigPO == null || !YesNoEnum.YES.isValue(merchantConfigPO.getState())) {
			throw PayExceptionCode.ERROR_ILL_PARAMETER.expMsg("merchantConfig 缺少配置,merchant:%s", merchantCode);
		}

		return YYFaxSignUtil.headerSign(merchantConfigPO.getSignKey(), FastJsonUtil.toJson(params));
	}
}
